package com.c.opt;

import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.TypeUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.PageResult;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.StaticLog;
import com.c.opt.query.ComOptQueryWrapper;
import com.c.opt.tool.ToolEntity;
import com.c.opt.tool.ToolQuery;
import lombok.Data;
import ntd.util.ToolCache;
import ntd.util.config.common.exception.ComException;
import ntd.util.frame.action.database.tool.ToolDb;
import ntd.util.frame.data.bo.MetaTableField;
import ntd.util.frame.data.factory.FactoryData;
import ntd.util.frame.data.tool.ToolMysql;
import ntd.util.util.metadata.UtilMetaDataTwo;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * 数据库操作能力封装
 */
@Data
public class ComOpt<T> implements ComOptInterface<T> {

    private String tName;
    private String dsId;
    private Class<T> clazz;

    public ComOpt(String tName, String dsId) {
        if (StrUtil.isBlank(tName)) {
            throw new ComException("ComOpt 构建失败！");
        }
        this.dsId = dsId;
        this.tName = tName;
        bean(null);
    }

    public ComOpt(String tName) {
        if (StrUtil.isBlank(tName)) {
            throw new ComException("ComOpt 构建失败！");
        }
        this.tName = tName;
        bean(null);
    }

    @Override
    public String tName() {
        return this.tName;
    }

    @Override
    public ComOpt<T> bean(Class<T> beanClass) {
        this.clazz = ObjectUtil.defaultIfNull(beanClass, (Class<T>) TypeUtil.getTypeArgument(getClass()));
        return this;
    }

    @Override
    public DataSource ds() {
        return FactoryData.dataSource(dsId);
    }

    /**
     * 获取 db 操作
     * @return
     */
    @Override
    public Db db() {
        return Db.use(ds());
    }

    /**
     * 获取 tool Mysql 操作
     * @return
     */
    @Override
    public ToolMysql toolMysql() {
        return new ToolMysql(ds());
    }

    /**
     * 表信息
     * @return
     */
    @Override
    public List<MetaTableField> tableInfo() {
        String key = this.dsId + this.tName;

        List<MetaTableField> metaTableFields = ToolCache.get(key);
        if (CollUtil.isEmpty(metaTableFields)) {
            metaTableFields = toolMysql().tableInfo(tName);
            metaTableFields.forEach(UtilMetaDataTwo::transformMetaDataBO);
            ToolCache.set(key, metaTableFields, 1000 * 30);
        }
        return metaTableFields;
    }

    
    @Override
    public List<Entity> list(ComOptQueryWrapper comOptQueryWrapper) {
        return this.page(ObjectUtil.defaultIfNull(comOptQueryWrapper, queryWrapper()), null, null);
    }

    @Override
    public Entity get(ComOptQueryWrapper comOptQueryWrapper) {
        if (comOptQueryWrapper.isEmpty()) {
            throw new ComException("数据库 Get 操作，条件不能为空！");
        }
        List<Entity> entities = list(comOptQueryWrapper);
        Entity entity = null;
        if (CollUtil.isNotEmpty(entities)) {
            if (entities.size() > 1) {
                throw new ComException("预期获取一个，实际上获取到多个，数据异常[{}]", comOptQueryWrapper.buildSql());
            }
            entity = entities.remove(0);
//            for (Entity row : entities) {
//                StaticLog.error("获取数据失败，预期获取1个，实际查询到多个，记录错误数据[{}]到日志!", JSONUtil.toJsonStr(row));
////                removeById(row.getStr("id"));
//            }
        }
        return entity;
    }

    @Override
    public Entity getById(String id) {
        if (StrUtil.isBlank(id)) {
            return null;
        }
        return get(query().eq("id", id));
    }

    @Override
    public List<T> listBean(ComOptQueryWrapper comOptQueryWrapper) {
        return CollStreamUtil.toList(list(comOptQueryWrapper), this::toBean);
    }

    @Override
    public T getBean(ComOptQueryWrapper comOptQueryWrapper) {
        return toBean(get(comOptQueryWrapper));
    }

    @Override
    public T getBeanById(String id) {
        return toBean(getById(id));
    }

    @Override
    public ComChainOpt<T> query() {
        return new ComChainOpt<>(this);
    }

    @Override
    public ComOptQueryWrapper queryWrapper() {
        return new ComOptQueryWrapper(tName);
    }

    @Override
    public Integer count(ComOptQueryWrapper comOptQueryWrapper) {
        try {
            return db().queryNumber(comOptQueryWrapper.buildCountSql()).intValue();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public PageResult<Entity> page(ComOptQueryWrapper comOptQueryWrapper, Integer page, Integer size) {
        ToolQuery.initDelFlag(comOptQueryWrapper, tableInfo());
        ToolQuery.initOrder(comOptQueryWrapper, tableInfo());
        ToolQuery.initDataLevel(comOptQueryWrapper, tableInfo());
        Db db = db();
        PageResult<Entity> pageResult = new PageResult<>();
        List<Entity> entities;
        String sql = null;
        try {
            TimeInterval timer = DateUtil.timer();
            if (ObjectUtil.isAllNotEmpty(page, size)) {
                pageResult.setTotal(db.queryNumber(comOptQueryWrapper.buildCountSql()).intValue());
                sql = comOptQueryWrapper.buildPageSql(page, size);
            } else {
                sql = comOptQueryWrapper.buildSql();
            }
            entities = db.query(sql);
            entities.forEach(ToolDb::fieldTransform);
            if (timer.interval() > 500) {
                StaticLog.error("慢sql[{}]异常[{}]", timer.interval(), sql);
            }
        } catch (Exception e) {
            StaticLog.error(e, "[ComOpt][数据查询][{}]失败...", sql);
            throw new ComException("数据查询异常，请联系管理员!");
        }
        pageResult.addAll(entities);
        return pageResult;
    }

    @Override
    public PageResult<T> pageBean(ComOptQueryWrapper comOptQueryWrapper, Integer page, Integer size) {
        PageResult<Entity> pageResult = page(comOptQueryWrapper, page, size);
        PageResult<T> result = new PageResult<>(pageResult.getPage(), pageResult.getPageSize(), pageResult.getTotal());
        if (CollUtil.isEmpty(pageResult)) {
            return result;
        }
        result.addAll(CollStreamUtil.toList(pageResult, this::toBean));
        return result;
    }

    
    @Override
    public boolean removeById(String id) {
        if (StrUtil.isBlank(id)) {
            StaticLog.error("删除[{}]-[{}]操作执行失败,", tName, id);
        }
        int execute = 0;
        try {
            execute = db().execute(StrUtil.format("update {} set flag = 1 where id = '{}'", tName, id));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return execute > 0;
    }

    @Override
    public String syncBean(T t) {
        return sync(JSONUtil.parseObj(ToolEntity.toEntity(t)));
    }

    @Override
    public int syncBean(T t, Entity where) {
        return sync(JSONUtil.parseObj(ToolEntity.toEntity(t)), where);
    }

    @Override
    public String syncEntity(Entity entity) {
        return sync(JSONUtil.parseObj(entity));
    }

    @Override
    public int syncEntity(Entity entity, Entity where) {
        return sync(JSONUtil.parseObj(entity), where);
    }

    
    @Override
    public String sync(Map<String, Object> syncData) {
        Entity entity = ToolEntity.toEntity(syncData, tableInfo(), null).setTableName(tName);
        try {
            Db db = db();
            if (StrUtil.isBlank(entity.getStr("id"))) {
                String id = StrUtil.toString(db.insertForGeneratedKey(entity));
                entity.set("id", id);
                return id;
            } else {
                db.insertOrUpdate(entity, "id");
            }
            return entity.getStr("id");
        } catch (Exception e) {
            StaticLog.error(e, "[ComOpt][数据同步][{}]失败...", JSONUtil.toJsonStr(entity));
            throw new ComException("数据同步失败，请联系管理员...");
        }
    }

    
    @Override
    public int sync(JSONObject syncData, Entity where) {
        if (where.isEmpty()) {
            throw new ComException("数据库 Update 操作，条件不能为空！");
        }
        try {
            return db().update(ToolEntity.toEntity(syncData, tableInfo(), false).setTableName(tName), where);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private T toBean(Entity entity) {
        return ToolEntity.toBean(entity, clazz);
    }
}
